//
//  PacketTunnelProvider.m
//  KDPacketTunelProvider
//
//  Created by YeQing on 2017/3/22.
//  Copyright © 2017年 naruto. All rights reserved.
//  滴滴 vpn协议组件

#import "PacketTunnelProvider.h"
#import "DiDiTunnelLog.h"

@interface PacketTunnelProvider ()<NWTCPConnectionAuthenticationDelegate>
@property (nonatomic, strong) NWUDPSession  * udpSession;
@property NWTCPConnection *connection;
@property (strong) void (^pendingStartCompletion)(NSError *);
@end

@implementation PacketTunnelProvider
#pragma mark - system
- (instancetype)init
{
    self = [super init];
    if (self) {
        
    }
    return self;
}

#pragma mark - override
- (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(NSError *))completionHandler
{
    if(!options){
        DDPTLogInfosStr(@"must open vpn in DiDi");
        NSError *error = [NSError errorWithDomain:@"PacketTunnelProviderDomain" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"must open vpn in DiDi" }];
        if(completionHandler){
            completionHandler(error);
        }
        else{
            [self cancelTunnelWithError:error];
        }
        return;
    }
    DDPTLogInfos(@"%@",options);
    NSString *serverAddr = self.protocolConfiguration.serverAddress;
    NSString *port = @"443";
    NWHostEndpoint *serverHost = [NWHostEndpoint endpointWithHostname:serverAddr port:port];
//    NWUDPSession *udpSession = [self createUDPSessionToEndpoint:peer fromEndpoint:nil];
//    self.udpSession = udpSession;
//    [udpSession setReadHandler:^(NSArray<NSData *> * _Nullable datagrams, NSError * _Nullable error) {
//        DiDiVPNTPLog(@"DDTP readPackets:%@",datagrams);
//    } maxDatagrams:8192];
//    [udpSession addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionInitial context:nil];
    
	NWTCPConnection *newConnection = [self createTCPConnectionToEndpoint:serverHost enableTLS:YES TLSParameters:nil delegate:self];
	[newConnection addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionInitial context:nil];
	self.connection = newConnection;
    
	self.pendingStartCompletion = completionHandler;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    DDPTLogInfos(@"%@,%@,%@",keyPath,object,change);
	if ([keyPath isEqualToString:@"state"]) {
		NWTCPConnection *conn = (NWTCPConnection *)object;
		if (conn.state == NWTCPConnectionStateConnected) {
			NWHostEndpoint *ra = (NWHostEndpoint *)conn.remoteAddress;
            __weak typeof(self) weakself = self;
            
            NSArray *addresses = @[@"172.30.100.191"];
            NSArray *subnetMasks = @[@"255.255.254.0"];
//            NSArray<NSString *> *dnsServers = @[@"8.8.8.8", @"8.8.4.4"];
            NEPacketTunnelNetworkSettings *settings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:ra.hostname];
            settings.IPv4Settings = [[NEIPv4Settings alloc] initWithAddresses:addresses subnetMasks:subnetMasks];
            NEIPv4Route *defaultRoute = [NEIPv4Route defaultRoute];
            NEIPv4Route *localRoute = [[NEIPv4Route alloc] initWithDestinationAddress:@"10.0.0.0" subnetMask:@"255.255.255.0"];
            settings.IPv4Settings.includedRoutes = @[defaultRoute, localRoute];
            settings.IPv4Settings.excludedRoutes = @[];
            settings.MTU = [NSNumber numberWithInt:1500];
            
			[self setTunnelNetworkSettings:settings completionHandler:^(NSError *error) {
				if (error == nil) {
					[weakself addObserver:weakself forKeyPath:@"defaultPath" options:NSKeyValueObservingOptionInitial context:nil];
                    [weakself readPackets];
					[conn readMinimumLength:0 maximumLength:8192 completionHandler:^(NSData *data, NSError *error) {
						// Add code here to parse packets from the data, call [self.packetFlow writePackets] with the result
                        DDPTLogInfo(@"readMini:%@,%@",data,[error userInfo]);
					}];
				}
				if (weakself.pendingStartCompletion != nil) {
					weakself.pendingStartCompletion(nil);
					weakself.pendingStartCompletion = nil;
				}
			}];
            
		}
        else if (conn.state == NWTCPConnectionStateDisconnected) {
			NSError *error = [NSError errorWithDomain:@"PacketTunnelProviderDomain" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"Connection closed by server" }];
			if (self.pendingStartCompletion != nil) {
				self.pendingStartCompletion(error);
				self.pendingStartCompletion = nil;
			} else {
				[self cancelTunnelWithError:error];
			}
			[conn cancel];
		}
        else if (conn.state == NWTCPConnectionStateCancelled) {
			[self removeObserver:self forKeyPath:@"defaultPath"];
			[conn removeObserver:self forKeyPath:@"state"];
			self.connection = nil;
		}
	}
    else if ([keyPath isEqualToString:@"defaultPath"]) {
		// Add code here to deal with changes to the network
	}
    else {
		[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
	}
}

- (void)stopTunnelWithReason:(NEProviderStopReason)reason completionHandler:(void (^)(void))completionHandler
{
    DDPTLogInfos(@"%ld",(long)reason);
	[self.connection cancel];
    [self.udpSession cancel];
	completionHandler();
}

- (void)handleAppMessage:(NSData *)messageData completionHandler:(void (^)(NSData *))completionHandler
{
	DDPTLogInfos(@"%@",messageData);
	if (completionHandler != nil) {
		completionHandler(messageData);
	}
}

- (void)sleepWithCompletionHandler:(void (^)(void))completionHandler
{
    DDPTLogInfosNull();
	completionHandler();
}

- (void)wake
{
    DDPTLogInfosNull();
	// Add code here to wake up
}


#pragma mark - 读取 packets
-(void)readPackets
{
    __weak typeof(self) weakself = self;
    [self.packetFlow readPacketsWithCompletionHandler:^(NSArray<NSData *> *packets, NSArray<NSNumber *> *protocols) {
        DDPTLogInfo(@"readPackets:%@,%@",packets,protocols);
//        for (NSData *packet in packets){
//        }

        [weakself readPackets];
    }];
    
    
}


#pragma mark - NWTCPConnectionAuthenticationDelegate
- (BOOL)shouldProvideIdentityForConnection:(NWTCPConnection *)connection
{
    return YES;
}
- (void)provideIdentityForConnection:(NWTCPConnection *)connection completionHandler:(void (^)(SecIdentityRef identity, NSArray<id> *certificateChain))completion
{
    
}
//- (BOOL)shouldEvaluateTrustForConnection:(NWTCPConnection *)connection
//{
//    return YES;
//}
//- (void)evaluateTrustForConnection:(NWTCPConnection *)connection peerCertificateChain:(NSArray<id> *)peerCertificateChain completionHandler:(void (^)(SecTrustRef trust))completion
//{
//    
//}
@end
